	'	Comms1
	'	======
	'	Melanie Newman
	'	16 July 2004
	'
	'	Demonstration of PC to PIC bi-directional Dialogue
	'	PC User (using something like Hyperterminal) is able to
	'	to control 5 LEDs using a number of Commands with sub-parameters
	'	shows a different approach to Comms in having the PIC parse
	'	a received command line rather than WAIT for something valid
	'	to arrive and latch on to.

	'	---------------------------------------------------------------------------
	'	This example for commection to PC at
	'	300 baud, 8 Data Bits, No Parity, 1 stop Bit, No Flow Control
	'	Set Hyperterminal (or whatever) to above parameters on your chosen COM port
	'	---------------------------------------------------------------------------

	'	Links with PC Comms Program such as Hyperterminal
	'	Echo's printable characters typed on keyboard back to PC
	'	Performs rudimentary editing with BACKSPACE character
	'	Executes the following Instructions;-
	'
	'	ON x y	Turn ON LED on PortA where x=1 to 5 for LED1-LED5
	'		if y is specified (value 1-9), will turn ON for y Seconds an then OFF
	'	ON	turn ON all LEDs
	'	ON 0	same as ON
	'	ON 0 y	turn ON all LEDs for y Seconds then OFF
	'
	'	OFF x y	Turn OFF LED on PortA where x=1 to 4 for LED1-LED4
	'		if y is specified (value 1-9), will turn OFF for y Seconds then ON
	'	OFF	turn OFF all LEDs
	'	OFF 0	same as OFF
	'	OFF 0 y	Turn OFF all LEDs for y Seconds then ON
	'
	'	BLINK x y
	'		Blink LED y number of times. If x=0 then Blink ALL LEDs y times
	'		valid range for y Blink is 1-60
	'
	'	SWEEP x
	'		valid range for x is 1-99.  If omitted defaults to 1.
	'
	'	BEER 	just for fun!
	'
	'	Program will respond to PC with OK on executing valid command
	'	Program will respond to PC with Bad! if Bad Command, Parameter or Parameter out of range
	'	PC User can type in UPPERCASE or lowercase or any cOmBinATioN

	'
	'	CPU Hardware Layout
	'	-------------------
	'
	'	PortA.0 - LED1-k to PIC pin, LED1-a to VDD via Resistor
	'	PortA.1 - LED2-k to PIC pin, LED2-a to VDD via Resistor
	'	PortA.2 - LED3-k to PIC pin, LED3-a to VDD via Resistor
	'	PortA.3 - LED4-k to PIC pin, LED4-a to VDD via Resistor
	'	PortA.4 - LED5-k to PIC pin, LED5-a to VDD via Resistor
	'	PortA.5 - N/c 
	'	PortA.6 - N/C
	'	PortA.7 - N/C
	'
	'	PortB.0	- N/C
	'	PortB.1 - RX cable via 1K5 Resistor to PC pin 3 on 9-Pin D Connector
	'	PortB.2 - TX cable via 1K5 Resistor to PC pin 2 on 9-Pin D Connector
	'	PortB.3 - N/C
	'	PortB.4 - N/C
	'	PortB.5 - N/C
	'	PortB.6 - N/C
	'	PortB.7 - N/C
	'
	'	Don't forget to connect Vss to PC pin 5 on 9-pin D Connector

	'
	' 	PIC Defines
	' 	-----------
	@ DEVICE pic16F628, INTRC_OSC_NOCLKOUT
		' System Clock Options	
	@ DEVICE pic16F628, WDT_ON
		' Watchdog Timer
	@ DEVICE pic16F628, PWRT_ON
		' Power-On Timer
	@ DEVICE pic16F628, MCLR_OFF
		' Master Clear Options (Internal)
	@ DEVICE pic16F628, BOD_ON
		' Brown-Out Detect
	@ DEVICE pic16F628, LVP_OFF
		' Low-Voltage Programming
	@ DEVICE pic16F628, CPD_OFF
		' Data Memory Code Protect
	@ DEVICE pic16F628, PROTECT_OFF
		' Program Code Protection

	'
	'	Debug (Communication) Defines (Transmit)
	'	----------------------------------------
	DEFINE DEBUG_REG PORTB
	DEFINE DEBUG_BIT 2
	DEFINE DEBUG_BAUD 300
	DEFINE DEBUG_MODE 1

	'
	'	DEBUG (Communication) Defines (Receive)
	'	----------------------------------------
	DEFINE DEBUGIN_REG PORTB
	DEFINE DEBUGIN_BIT 1
	DEFINE DEBUGIN_BAUD 300
	DEFINE DEBUGIN_MODE 1

	'
	'	EEPROM Data 
	'	-----------
	'	The FIRST character of each line holds the Command Word LENGTH
	'	and the remaining 7 characters are the actual COMMAND WORD
	'	Space allocated for 10 commands although I'm only using the first five
	'	See CONSTANT CommandMax if you want to make the table larger or smaller
	'	Commands here should be in UPPERCASE although the PC can send aNyThiNg.

	'
	Data	@0,2,"ON",0,0,0,0,0
	Data	3,"OFF",0,0,0,0
	Data	5,"BLINK",0,0
	Data	5,"SWEEP",0,0
	Data	4,"BEER",0,0,0
	Data	0,0,0,0,0,0,0,0
	Data	0,0,0,0,0,0,0,0
	Data	0,0,0,0,0,0,0,0
	Data	0,0,0,0,0,0,0,0
	Data	0,0,0,0,0,0,0,0

	'
	'	RAM Assignments and Variables
	'	-----------------------------
	BadFlag var BIT			' Flag for Bad Command
	CounterA var BYTE		' Just a Counter
	CounterB var BYTE		' Just a Counter
	CounterC var BYTE		' Just a Counter
	CounterW var WORD		' Word-sized Counter
	Command var BYTE		' Holds the Command action to execute
	CommsBuffer var BYTE [10]	' Max Comms Command Line size
	CommsPointer var BYTE		' Pointer to data in Comms Buffer
	LEDStatus var BYTE
	DataA var BYTE			' Usage as Data Byte
	Xvariable var BYTE		' 1st Numeric Variable
	Yvariable var BYTE		' 2nd Numeric Variable

	'
	'	Program Constants
	'	-----------------
	BufMax con 10			' Max size of data Buffer
	CommandMax con 10		' Max number of COMMANDS to be processed


	'
	'	Start - Initialise Processor
	'	============================
	TRISA=%00000000
	TRISB=%00000010
	Xvariable=0:Gosub LEDsOFF	' All LEDs OFF
	'
	'	Clear Buffer ready for start of New Command Line
	'	------------------------------------------------
RestartLine:
	For CounterA=1 to BufMax
		CommsBuffer[CounterA-1]=0
		Next CounterA
	CommsPointer=0			' Set Pointer to Start of Buffer
	'
	'	Communications (Closed) Loop
	'	----------------------------
ReadLoop:
	DEBUGIN [DataA]
	If DataA<32 then 
		If DataA=8 then 				' Process BACKSPACE Key
			If CommsPointer>0 then 			' ---------------------
				CommsPointer=CommsPointer-1	' Rewind one position
				CommsBuffer(CommsPointer)=0	' Erase character in Buffer
				DEBUG $08,$20,$08		' Echo Erase sequence to PC
				Goto ReadLoop
				endif
			endif
		If DataA=13 then goto ENTERKey
		Goto ReadLoop
		endif
	If DataA>126 then goto ReadLoop
	DEBUG DataA			' Only Echo Printable Characters
	If DataA>96 then		' Convert all alphabetics to UPPERCASE
		If DataA<123 then DataA=DataA-32
		endif
	CommsBuffer(CommsPointer)=DataA	' Save to Buffer
	CommsPointer=CommsPointer+1	' Bump Pointer
	If CommsPointer<10 then goto ReadLoop
					' Loop for next character
	'
	'	Process [ENTER] Key
	'	-------------------
	'	also execute if Command Buffer exceeded
ENTERKey:
		'
		'	This section Parses the Command Line
		'	====================================
			'
			'	Look for Command WORD 
			'	(scan through available Command vocabulary)
			'	-------------------------------------------
	Gosub LSpace			' Remove Leading Blanks
	Gosub REnd			' Locate Command Word End
	If CounterA=0 then goto EchoCRLF
					' Nothing to Process
	For Command=1 to CommandMax
		BadFlag=0
		CounterB=(Command-1)*8
		Read CounterB,DataA	' Get Preset Command word Length
		If DataA=CounterA then	' If Length correct, process entire word
			For CounterC=1 to CounterA
				CounterB=CounterB+1
				Read CounterB,DataA
				If CommsBuffer(CounterC-1)<>DataA then BadFlag=1
				Next CounterC
			If BadFlag=0 then goto GoodCommand
			endif
		Next Command
			'
			'	Bad Command Word
			'	----------------
BadMove:
	DEBUG REP $00\8,13,10,"Bad!"
					' Tell User No Cigar
	
EchoCRLF:
	DEBUG 13,10			' New Line
	Goto RestartLine		' Loop Round and Do again

			'
			'	Scan for First Numeric Variable (if any)
			'	----------------------------------------
GoodCommand:
	Gosub ShuffleData		' Advance Command Line to start of next Word (if any)
	Gosub REnd			' Locate End of next word
	Gosub GetNumericWord		' Get numeric value
	If BadFlag=1 then goto BadMove	' Invalid Data
	Xvariable=DataA			' Save 1st Numeric Variable
			'
			'	Now check for 2nd Numeric Variable (if any) on the Command Line
			'	---------------------------------------------------------------
	Gosub ShuffleData		' Advance Command Line to start of next (last) Word (if any)
	Gosub REnd			' Locate End of word
	Gosub GetNumericWord
	If BadFlag=1 then goto BadMove	' Invalid Data
	Yvariable=DataA			' Save 2nd Numeric Variable
	'
	'	Execute Command
	'	---------------
	If (Command<4 and Xvariable>5) then
		DEBUG REP $00\8,13,10,"No such LED!"
		goto EchoCRLF
		endif
	If Command<3 then
		If Yvariable>9 then goto BadMove
		If Yvariable>1 then gosub WaitMessage
		endif
	If Command=3 then
		If Yvariable>60 then goto BadMove
		If Yvariable>5 then gosub WaitMessage
		endif
	If Command=4 then
		If Xvariable>5 then gosub WaitMessage
		endif
		'
		'	ON LED Command
		'	--------------
	If Command=1 then 
		Gosub LEDsON
		If Yvariable>0 then
			CounterW=Yvariable*1000
			Pause CounterW
			Gosub LEDsOFF
			endif
		endif
		'
		'	OFF LED Command
		'	---------------
	If Command=2 then 
		Gosub LEDsOFF
		If Yvariable>0 then
			CounterW=Yvariable*1000
			Pause CounterW
			Gosub LEDsON
			endif
		endif
		'
		'	BLINK LED Command
		'	-----------------
	If Command=3 then
		For CounterA=1 to Yvariable
			Gosub LEDsON
			Pause 200
			Gosub LEDsOFF
			Pause 200
			Next CounterA
		endif
		'
		'	SWEEP Command
		'	-------------
	If Command=4 then
		CounterB=Xvariable
		If CounterB<1 then CounterB=1
		For CounterA=1 to CounterB
			For Xvariable=1 to 5
				Gosub LEDsON
				Pause 100
				Gosub LEDsOFF
				Next XVariable
			Next CounterA
		endif
		'
		'	BEER Command
		'	------------
	If Command=5 then DEBUG REP $00\8,13,10,"Yes Please!"
	'
	'	Tell User all executed OK
	'	-------------------------
	DEBUG REP $00\8,13,10,"OK"
	Goto EchoCRLF

	'
	'	Subroutine Area
	'	===============

	'
	'	Subroutine extracts Numeric Value from Command Line
	'	---------------------------------------------------
GetNumericWord:
	DataA=0
	BadFlag=0
	If CounterA>2 then Goto BadWord
	If CounterA>0 then
		CounterB=CommsBuffer(0)
		If CounterB<48 then goto BadWord
		DataA=CounterB-48
		If DataA>9 then goto BadWord
		If CounterA=2 then
			CounterB=CommsBuffer(1)
			If CounterB<48 then goto BadWord
			CounterB=CounterB-48
			If CounterB>9 then goto BadWord
			DataA=DataA*10+CounterB
			endif
		endif
	Return
BadWord:
	BadFlag=1
	Return

	'
	'	Shuffle Data to remove processed word
	'	-------------------------------------
ShuffleData:
	For CounterB=0 to 9
		CounterC=CounterA+CounterB
		If CounterC<=9 then
			CommsBuffer(CounterB)=CommsBuffer(CounterC)
			else
			CommsBuffer(CounterB)=0
			endif
		Next CounterB
	'
	'	Subroutine removes leading SPACES (Blanks) from Buffer
	'	------------------------------------------------------
LSpace:
	If CommsBuffer(0)=32 then
		For CounterA=1 to 9
			CommsBuffer(CounterA-1)=CommsBuffer(CounterA)
			Next CounterA
		CommsBuffer(9)=0
		Goto LSpace
		endif
	Return

	'
	'	Subroutine Locates end of Dataword in Comms Buffer
	'	--------------------------------------------------
	'	CounterA on exit End Point
REnd:	
	For CounterA=0 to 9
		If CommsBuffer(CounterA)=32 then goto REndExit
		If CommsBuffer(CounterA)=0 then goto REndExit
		Next CounterA
	CounterA=10
REndExit:
	Return

	'
	'	Subroutine tells User to Wait
	'	-----------------------------
WaitMessage:
	DEBUG REP $00\8,13,10,"Wait..."
	Return

	'
	'	Subroutine DISABLES LEDs
	'	------------------------
LEDsOFF:
	If Xvariable=0 then
		LEDStatus=$1F
		else
		LEDStatus.0(Xvariable-1)=1
		endif
	PortA=LEDStatus
	Return

	'
	'	Subroutine ENABLES LEDs
	'	-----------------------
LEDsON:
	If Xvariable=0 then
		LEDStatus=$0
		else
		LEDStatus.0(Xvariable-1)=0
		endif
	PortA=LEDStatus
	Return

	End

